#include <linux/input.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <signal.h>
#include <fcntl.h>

int fd ; // 文件句柄
/* ./input_read_faync /dev/input/event0 */
/*
 * 查询方式：有数据时立刻返回，没有数据时返回错误
 * 在open时传入nonblock，表示以非阻塞的方式打开
 * 休眠唤醒方式：有数据时驱动程序唤醒APP，返回数据，没有数据时休眠
 * 在open时不传入nonblock，表示以阻塞的方式打开
 * poll唤醒方式：在一定时间内如果有数据传入，则返回数据给APP，否则就休眠，如果在规定时间没有数据则返回超时
 ****异步通知方式：驱动程序慢，APP先做自己的事情，驱动程序有输入了自己发送信号给APP
 ****发什么信号？ SIGIO
 ****驱动程序怎么知道发给谁？ APP把自己的进程号注册给驱动程序
 ****APP收到信号做什么？  执行信号处理函数
 ****信号处理函数和信号怎么挂钩？  APP注册信号处理函数sighandle_t signal(int signum,sighandle_t handler);
 * 同步：你慢我就一直等你
 */

/*1.注册信号处理函数signal(int signum,sighandle_t handler)*/
/*2.打开驱动程序open*/
/*3.把APP进程号告诉驱动程序*/
/*4.使能异步通知*/

/*信号处理函数typedef void (*sighandle_t) (int);*/
void MY_signal_handler (int sig)
{
    struct input_event event ; // 存放设备输入信息的对象
    while(read(fd , &event , sizeof(event)))
    {
        printf("get event type = 0x%x ,code = 0x%x ,value = 0x%x \n" , event.type, event.code, event.value);
    }
}


int main(int argc, char **argv)
{

    int count ; // 计数值
    int err ; // ioctl的错误信息
    int len ; // 获取设备evbit的大小
    int i ;
    int byte ; // 字节
    int bit ; // 位
    unsigned int flag ; // 异步通知标志位
    struct input_id id ; // 存放设备信息的对象
    unsigned int evbit[2] ; // 存放设备evbit数据的数组
    char *ev_names[] = {// 定义一个指针数组，存放支持事件的名称
		"EV_SYN ",
		"EV_KEY ",
		"EV_REL ",
		"EV_ABS ",
		"EV_MSC ",
		"EV_SW	",
		"NULL ",
		"NULL ",
		"NULL ",
		"NULL ",
		"NULL ",
		"NULL ",
		"NULL ",
		"NULL ",
		"NULL ",
		"NULL ",
		"NULL ",
		"EV_LED ",
		"EV_SND ",
		"NULL ",
		"EV_REP ",
		"EV_FF	",
		"EV_PWR ",
		} ;


    /*判断传参是否正确是否*/
    if(argc != 2)
    {
        printf("Usage:%s <dev>\r\n", argv[0]);
        return -1;
    }

    /*注册信号处理函数signal(int signum,sighandle_t handler)*/
    signal(SIGIO,MY_signal_handler);

    /*打开驱动程序open*/
    fd = open(argv[1], O_RDWR , O_NONBLOCK);   

    if(fd < 0)
    { // 判断打开是否成功
        printf("%s err\r\n", argv[1]);
        return -1;
    }

    /*获取设备信息并打印*/
    err = ioctl(fd , EVIOCGID , &id);
    if (err == 0)
    {
    /*  EVIOCGID获取的信息
        __u16 bustype;//总线类型
        __u16 vendor;//厂商
        __u16 product;
        __u16 version;
    */
        printf("bustype = 0x%x\n" , id.bustype);
        printf("vendor = 0x%x\n" , id.vendor);
        printf("product = 0x%x\n" , id.product);
        printf("version = 0x%x\n" , id.version);
    }

    /*获取设备evbit，即支持哪些事件*/
    len = ioctl(fd, EVIOCGBIT(0,sizeof(evbit)) , &evbit);
    if (len > 0 && len < sizeof(evbit))
    {
        printf("support ev type\n");
        for(i = 0; i < len; i++)
        {
            byte = ((unsigned char*)evbit)[i];// 将evbit中的一个字节的数据写入byte
            for(bit = 0 ; bit<8 ; bit++ ) 
            {
                if(byte & (1<<bit))
                {// 判断byte中哪一位是等于1的
                    printf("%s" , ev_names[i*8+bit]);// 打印出对应位的信息
                }
            }
        }
        printf("\n");
    }

    /*把APP进程号告诉驱动程序*/
    fcntl(fd,F_SETOWN, getpid());

    /*使能异步通知*/
    flag = fcntl(fd,F_GETFL);
    fcntl(fd,F_SETFL, flag | FASYNC);

    /*获取输入信息*/
    while(1)
    {
        printf("main loop cout:%d",count++);
        sleep(2);
    }
    return 0;
}